//
//	File Name   :	fbxLoader.cpp
//	Description :	fbxLoader implementation file.
//	Author		  :	William McVicar
//  Mail		    :	mcvicar.william@gmail.com
//

//  Precompiled Header
#include "CommonDefines.h"

//  Library Includes

//  Local Includes
#include "../ProteinFramework.h"
#include "../IFileSystem.h"
#include "../math/Vector2.h"
#include "../math/Vector3.h"
#include "../math/Vector4.h" 

//  This includes
#include "fbxloader.h"

//	Macros

//  Static Variables
fbxsdk_2013_2::FbxManager* Protein::FBXLoader::s_fbxSdkManager = NULL;

//  Static Function Prototypes

//  Implementation

namespace Protein
{

FBXLoader::FBXLoader()
{

}

FBXLoader::~FBXLoader()
{
	if( s_fbxSdkManager )
	{
		s_fbxSdkManager->Destroy();
		s_fbxSdkManager = NULL;
	}
}

bool FBXLoader::LoadFBX( const char* _szFilename, tFBXData& data )
{
	std::string strPath;
	if( IFileSystem::FILESYSTEM_ERROR_FILE_NOT_FOUND == fileSystem().FileExists( _szFilename, strPath ) )
		return false;

	if( !s_fbxSdkManager )
	{
		s_fbxSdkManager = FbxManager::Create();
		if( !s_fbxSdkManager )
			return false;

		FbxIOSettings* ios = FbxIOSettings::Create( s_fbxSdkManager, IOSROOT );
		s_fbxSdkManager->SetIOSettings( ios );
	}

	int lFileMajor, lFileMinor, lFileRevision;
    int lSDKMajor,  lSDKMinor,  lSDKRevision;
    bool lStatus;
	fbxsdk_2013_2::FbxScene* scene = fbxsdk_2013_2::FbxScene::Create(s_fbxSdkManager,"");

    // Get the version number of the FBX files generated by the
    // version of FBX SDK that you are using.
    FbxManager::GetFileFormatVersion(lSDKMajor, lSDKMinor, lSDKRevision);

    // Create an importer.
	FbxImporter* lImporter = FbxImporter::Create(s_fbxSdkManager,"");
    
    // Initialize the importer by providing a filename.
	const bool lImportStatus = lImporter->Initialize(strPath.c_str(), -1, s_fbxSdkManager->GetIOSettings() );

    // Get the version number of the FBX file format.
    lImporter->GetFileVersion(lFileMajor, lFileMinor, lFileRevision);

    if( !lImportStatus )  // Problem with the file to be imported
    {
		OutputDebugString(L"Call to FbxImporter::Initialize() failed.");
        //OutputDebugString("Error returned: %s", lImporter->GetLastErrorString());

        if (lImporter->GetLastErrorID() ==
            FbxIOBase::eFileVersionNotSupportedYet ||
            lImporter->GetLastErrorID() ==
            FbxIOBase::eFileVersionNotSupportedAnymore)
        {
            //UI_Printf("FBX version number for this FBX SDK is %d.%d.%d",
                //lSDKMajor, lSDKMinor, lSDKRevision);
            //UI_Printf("FBX version number for file %s is %d.%d.%d",
                //pFilename, lFileMajor, lFileMinor, lFileRevision);
        }

        return false;
    }

	lStatus = lImporter->Import( scene );
	if( !lStatus )
		return false;

	lImporter->Destroy();

	FbxNode* root = scene->GetRootNode();

	int numChildren = root->GetChildCount();
	FbxNode* child = NULL;
	for( int i = 0; i < numChildren; i++) 
	{
		child = root->GetChild(i);
		FbxMesh* mesh = child->GetMesh();
		if (mesh == NULL) 
		{
			printf ("Mesh doesn't exist\n");
			continue;
		}
		else 
		{
			if( !mesh->IsTriangleMesh() )
			{
				FbxGeometryConverter conv( s_fbxSdkManager );
				mesh = conv.TriangulateMesh( mesh );
				if( !mesh )
					return false; // some kind of error;
			}

			loadGeometryData( mesh, data );
			loadTextureData ( mesh, data );
			loadNormalData  ( mesh, data );
			
		} 
	}

	scene->Destroy();

	return true;
}

void FBXLoader::loadGeometryData( FbxMesh* mesh, tFBXData& data )
{
	data.numVerts	= mesh->GetControlPointsCount();
	data.numIndices = mesh->GetPolygonCount() * 3;
	data.pVerts		= new Vector3f[ data.numVerts ];
	data.pInds		= new u32[ data.numIndices ];

	i32* indices = mesh->GetPolygonVertices();
	memcpy( &data.pInds[0], &indices[0], sizeof(u32) * data.numIndices ); 

	for( int i = 0; i < data.numVerts; ++i )
	{
		FbxVector4 coord = mesh->GetControlPointAt(i);
		data.pVerts[i].set( float(coord.mData[0]), float(coord.mData[1]), float(coord.mData[2]) );
	}
}

bool FBXLoader::loadTextureData( FbxMesh* mesh, tFBXData& data )
{
	int numUVElements = mesh->GetElementUVCount();
	if( numUVElements <= 0 )
		return false; //no texture data;

	for( int i = 0; i < numUVElements; ++i )
	{
		FbxGeometryElementUV* UVElement = mesh->GetElementUV(i);
		if( !UVElement )
			continue;

		data.numTexVerts = UVElement->GetDirectArray().GetCount();
		data.pTexVerts	 = new Vector2f[ data.numTexVerts ];

		FbxVector2 vec2;
		for(int j = 0; j < data.numTexVerts; ++j)
		{
			vec2 = UVElement->GetDirectArray().GetAt(j);
			data.pTexVerts[j].set( float(vec2[0]), -float(vec2[1]) );
		}
	}

	return true;
}

bool FBXLoader::loadNormalData( FbxMesh* mesh, tFBXData& data )
{
	int numNormalElements = mesh->GetElementNormalCount();
	if( numNormalElements <= 0 )
		return false; //no normals

	data.numNormals = mesh->GetPolygonCount()*3;
	data.pNormals	= new Vector3f[data.numNormals];

	for( int i = 0; i < numNormalElements; ++i )
	{
		FbxGeometryElementNormal* normalElement = mesh->GetElementNormal();
		if( !normalElement )
			continue;
		
		int vertexCounter = 0;
		FbxVector4 normal;
		for(int polyCounter = 0; polyCounter < mesh->GetPolygonCount(); ++polyCounter)
		{
			normal = normalElement->GetDirectArray().GetAt(vertexCounter);
			data.pNormals[vertexCounter+0].set( float(normal[0]), float(normal[1]), float(normal[2]) );

			normal = normalElement->GetDirectArray().GetAt(vertexCounter+1);
			data.pNormals[vertexCounter+1].set( float(normal[0]), float(normal[1]), float(normal[2]) );

			normal = normalElement->GetDirectArray().GetAt(vertexCounter+2);
			data.pNormals[vertexCounter+2].set( float(normal[0]), float(normal[1]), float(normal[2]) );
			vertexCounter+=3;
		}
	}

	return true;
}

}